昨天學會了位元運算「旭賦忑」,今天就能來看看Math.imul()
到底怎麼做32位元的乘法吧!
我們用簡單的範例來模擬一次平移乘法的運算吧!
假設我們要計算3
乘以6
,用位元運算的方式,首先我們要將他們視為二進位的數字:
console.log(3 .toString(2)); //'11'
console.log(6 .toString(2)); //'110'
console.log(12 .toString(2)); //'10010'
用toString(2)
就能將數字轉為二進位表示。
我們現在知道3 * 6 = 18
也就是11
乘上110
等於10010
要怎麼解讀呢,我這邊提供一個方法讓大家比較好理解,也就是「分配律」。
我們知道數字向左移動一個單位就是將這個數乘以二,移動兩個單位就是乘以四,但是今天想做到的是乘以六呢?要如何平移才會是乘以六呢???
我們回想一下十進位的乘法,假設我們要做23 * 45
我們可以怎麼做呢?沒錯,打開計算機按下去。,先把45
拆成40 + 5
,然後做(23 * 40) + (23 * 5) = 920 + 115 = 1035
,理解了吧!聰明的你肯定理解了乘法可以將每個位數拆開來乘再加起來!
那讓我們回到二進位吧!
回到11
乘以110
,將乘數110
分解成100 + 10
,再利用分配律乘進去,也就是乘以2^2
跟2^1
,那這時候乘以二的次方我們就可以用「向左平移」來處理了!2^2
就是向左平移兩單位;2^1
就是向左平移一單位,將他們加起來就是乘以110
的結果了!
所以我們可以記成(0b11 << 2) + (0b11 << 1)
console.log((0b11 << 2) + (0b11 << 1)); //18
成功了!我們成功算出正確答案了~~
再來簡單包裝成函式吧!
function shiftMultiply(multiplicand, multiplier) {
let result = 0;
let shift = 0;
while(multiplier > 0) {
if(multiplier & 1) {
result += multiplicand << shift
}
shift++;
multiplier >>= 1;
}
return result;
}
console.log(shiftMultiply(3, 6)); //18
首先令結果result
為0
,向左平移的量shift
也為0
,接著判斷當乘數不為0
的時,近一步判斷要不要平移,這邊用了& 1
來判斷最右的位元是否為1
,是就平移之後加進result
,接著shift
加一、乘數向右平移將剛剛判斷完的位元擠掉,繼續判斷下個位元。全部平移完就回傳result
,這是一個簡單實現32位元乘法的範例。
但開頭其實要說的是Math.imul()
對吧?實際上我們少了幾個要點:溢位時的處理,負數的計算等等。不過別在意!我們今天也成功學到了如何實作二進位的乘法了!應該也算是半個Math.imul()
吧...
同場加映:
如果各位對什麼時候要加一、什麼時候要終止迴圈覺得很難控制,這邊提供另一個奇妙的做法,使用array method
~~但就會需要先將要處理的資料用split
轉成陣列,然後我們就能盡情的揮灑array
咒語啦~
這邊就留給大家參考參考:
function shiftMultiple(twoBitNumber, multiplier) {
return multiplier .toString(2)
.split('')
.map(str => Number(str))
.reverse()
.reduce((sum, shiftable, shiftTimes, numberStr) => {
if (shiftable) {
sum.result += twoBitNumber << shiftTimes;
return sum
}
else sum;
}, {origin: twoBitNumber, result: 0})
}
shiftMultiple(4, 3);
有任何想法也歡迎大家底下留言一起討論喔!
各位明天見~
參考資料:
Number.prototype.toString()